package net.siufung.simple.security.sevice.impl;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.google.common.collect.Lists;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import net.siufung.core.exception.SiufungServiceException;
import net.siufung.core.model.enums.DeletedEnum;
import net.siufung.market.base.menu.controller.model.vo.MenuTreeNode;
import net.siufung.market.base.menu.mapper.IMenuMapper;
import net.siufung.market.base.menu.mapper.IPermitMapper;
import net.siufung.market.base.menu.mapper.entity.Menu;
import net.siufung.market.base.menu.mapper.entity.Permit;
import net.siufung.market.base.user.mapper.IRolePermitMapper;
import net.siufung.market.base.user.mapper.IUserRoleMapper;
import net.siufung.market.base.user.mapper.entity.RolePermit;
import net.siufung.market.base.user.mapper.entity.UserRole;
import net.siufung.security.api.AuthUtil;
import net.siufung.simple.security.sevice.ISecurityService;
import org.apache.logging.log4j.util.Strings;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import java.util.TreeSet;
import java.util.stream.Collectors;

/**
 * @author 陈建峰
 * @since 2022/7/7 10:45 下午
 */
@Slf4j
@Component("securityService")
@AllArgsConstructor
public class SecurityServiceImpl implements ISecurityService {

    private final IMenuMapper menuMapper;
    private final IPermitMapper permitMapper;
    private final IUserRoleMapper userRoleMapper;
    private final IRolePermitMapper rolePermitMapper;

    @Override
    public List<Menu> getCurrentMenuList() {
        List<Menu> allMenuList = menuMapper.selectList(
                new LambdaQueryWrapper<Menu>()
                        .eq(Menu::getDeleted, DeletedEnum.NOT.getValue()));
        List<Menu> currentMenuList = Lists.newCopyOnWriteArrayList(allMenuList);
        if(!AuthUtil.getSuperState()){
            List<Permit> currentPermitList = this.getCurrentPermitList();
            if(CollectionUtils.isEmpty(currentPermitList)){
                throw new SiufungServiceException("当前用户未分配权限！");
            }
            List<Integer> currentMenuIdList = currentPermitList.stream()
                    .map(Permit::getMenuId).distinct()
                    .collect(Collectors.toList());
            currentMenuList.clear();
            currentMenuList = menuMapper.selectBatchIds(currentMenuIdList);
            List<Integer> parentIdList;
            List<Menu> parentMenuList = Lists.newLinkedList(currentMenuList);
            do {
                parentIdList = parentMenuList.stream().map(Menu::getParentId).distinct()
                        .collect(Collectors.toList());
                final List<Integer> menuIdList = Lists.newArrayList(parentIdList);
                parentMenuList = allMenuList.stream()
                        .filter(menu -> menuIdList.contains(menu.getId()))
                        .collect(Collectors.toList());
                currentMenuList.addAll(parentMenuList);
            } while (ObjectUtil.isNotEmpty(parentIdList));
            return currentMenuList.stream()
                    .collect(Collectors.collectingAndThen(Collectors.toCollection(() ->
                            new TreeSet<>(Comparator.comparing(Menu::getId))), ArrayList::new));
        }
        return currentMenuList;
    }

    @Override
    public List<MenuTreeNode> getCurrentMenuTree() {
        List<Menu> currentMenuList = this.getCurrentMenuList();
        MenuTreeNode menuTreeNode;
        List<MenuTreeNode> menuTreeNodeList = Lists.newArrayList();
        for (Menu menu : currentMenuList) {
            menuTreeNode = new MenuTreeNode(
                    menu.getId(),
                    ObjectUtil.isNotNull(menu.getParentId())?menu.getParentId():-1,
                    menu.getMenuName(),
                    menu.getSequence(),
                    menu.getMenuCode(),
                    menu.getIcon(),
                    menu.getRouter(),
                    StrUtil.EMPTY,
                    StrUtil.EMPTY,
                    menu.getKeepAlive(),
                    "menu",
                    menu.getLevel(),
                    menu.getDeleted()
            );
            menuTreeNodeList.add(menuTreeNode);
        }
        return menuTreeNodeList;
    }

    @Override
    public List<Permit> getCurrentPermitList() {
        String tenantId = AuthUtil.getTenantId();
        if (AuthUtil.getSuperState()) {
            return permitMapper.selectList(new LambdaQueryWrapper<Permit>()
                    .eq(Permit::getDeleted, false));
        }
        List<UserRole> userRoleList = userRoleMapper.selectList(
                new LambdaQueryWrapper<UserRole>()
                        .eq(UserRole::getUserId, AuthUtil.getUserId())
                        .eq(!Strings.isBlank(tenantId), UserRole::getTenantId, tenantId));
        if (!CollectionUtils.isEmpty(userRoleList)) {
            throw new SiufungServiceException("当前用户未分配角色！");
        }
        List<Integer> roleIdList = userRoleList.stream()
                .map(UserRole::getRoleId).distinct()
                .collect(Collectors.toList());
        List<RolePermit> rolePermitList = rolePermitMapper.selectList(
                new LambdaQueryWrapper<RolePermit>()
                        .in(RolePermit::getRoleId, roleIdList)
                        .eq(!Strings.isBlank(tenantId), RolePermit::getTenantId, tenantId));
        List<Integer> permitIdList = rolePermitList.stream()
                .map(RolePermit::getPermitId).distinct().collect(Collectors.toList());
        List<Permit> permitList = Lists.newArrayList();
        if (!CollectionUtils.isEmpty(permitIdList)) {
            permitList = permitMapper.selectBatchIds(permitIdList);
            permitList = permitList.stream()
                    .filter(systemPermit -> !Objects.isNull(systemPermit.getMenuId()))
                    .distinct()
                    .collect(Collectors.toList());
        }
        return permitList;
    }
}
